package cn.sell.mbg.sys.sequence.factory;

import cn.sell.mbg.sys.sequence.SysSequence;
import cn.sell.mbg.sys.sequence.holder.MysqlSequenceHolder;
import cn.sell.mbg.sys.sequence.mapper.SequenceMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Component
public class MysqlSequenceFactory {

    private final Lock lock = new ReentrantLock();

    /**
     *
     */
    private Map<String, MysqlSequenceHolder> holderMap = new ConcurrentHashMap<>();

    @Autowired
    private SequenceMapper sequenceMapper;

    /**
     * 单个sequence初始化乐观锁更新失败重试次数
     */
    @Value("${seq.init.retry:5}")
    private int initRetryNum;

    /**
     * 单个 sequence 更新序列区间乐观锁更新失败重试次数
     */
    @Value("${seq.get.retry:20}")
    private int getRetryNum;

    @PostConstruct
    private void init() {
        //初始化所有sequence
        initAll();
    }


    /**
     * <p> 加载表中所有sequence，完成初始化 </p>
     *
     * @return void
     * @author coderzl
     */
    private void initAll() {
        try {
            lock.lock();
            List<SysSequence> boList = sequenceMapper.selectAll();
            if (boList == null) {
                throw new IllegalArgumentException("The sequenceRecord is null![sequence尚未配置数据]");
            }
            for (SysSequence bo : boList) {
                MysqlSequenceHolder holder = new MysqlSequenceHolder(sequenceMapper, bo, initRetryNum, getRetryNum);
                holder.init();
                holderMap.put(bo.getName(), holder);
            }
        } finally {
            lock.unlock();
        }
    }


    /**
     * <p>  </p>
     *
     * @param seqName
     * @return long
     * @author coderzl
     */
    public String getNextVal(String seqName) {
        MysqlSequenceHolder holder = holderMap.get(seqName);
        if (holder == null) {
            try {
                lock.lock();
                holder = holderMap.get(seqName);
                if (holder != null) {
                    return holder.getNextVal();
                }
                SysSequence sysSequence = new SysSequence();
                sysSequence.setName(seqName);
                sysSequence = sequenceMapper.selectOne(sysSequence);
                holder = new MysqlSequenceHolder(sequenceMapper, sysSequence, initRetryNum, getRetryNum);
                holder.init();
                holderMap.put(seqName, holder);
            } finally {
                lock.unlock();
            }
        }
        return holder.getNextVal();
    }

}
